#ifndef LIBACCENT_UTILITY_LIMIT_TRAVERSAL_HPP
#define LIBACCENT_UTILITY_LIMIT_TRAVERSAL_HPP

#include "accent/core.hpp"
#include "accent/utility/safe_position.hpp"
#include "accent/utility/tag_functions.hpp"
#include <cstddef>
#include <type_traits>

namespace accent
{

  namespace detail
  {

    template <typename Wrapped, typename Traversal>
    class limited_traversal
    {
      static_assert(std::is_same<Traversal,
                       support::minimum_tag<Traversal,
                            typename Wrapped::traversal>>::value,
          "Cannot force traversal higher than it is.");

      Wrapped wrapped;

    public:
      typedef Traversal traversal;

      limited_traversal(Wrapped wrapped) : wrapped(wrapped) {}

      bool empty() const { return wrapped.empty(); }
      auto front() const -> decltype(wrapped.front()) {
        return wrapped.front();
      }
      void pop_front() { wrapped.pop_front(); }

      typedef support::safe_position<Wrapped> position;

      position front_position() const { return wrapped.front_position(); }
      limited_traversal from(position p) const {
        return { wrapped.from(p) };
      }
      limited_traversal until(position p) const {
        return { wrapped.until(p) };
      }

      position back_position() const { return wrapped.back_position(); }
      auto back() const -> decltype(wrapped.front()) { return wrapped.back(); }
      void pop_back() { wrapped.pop_back(); }

      std::size_t size() const { return wrapped.size(); }
      void skip_front(std::size_t n) { wrapped.skip_front(n); }
      void skip_back(std::size_t n) { wrapped.skip_back(n); }
      auto operator [](std::size_t n) const -> decltype(wrapped.front()) {
        return wrapped[n];
      }
    };

  }

  template <typename Traversal, typename Wrapped>
  detail::limited_traversal<Wrapped, Traversal>
    limit_traversal(Wrapped wrapped, Traversal = Traversal())
  {
    return { wrapped };
  }

}

#endif
